package com.wjw.java.map;

import java.util.HashMap;
import java.util.Map;

/**
 * HashMap是Map接口的实现类
 * ------------------------------------------------------------------
 * 1.HashMap存放的元素是键值对：K V
 * 2.HashMap中的key可以为null,但只能有一个
 *   value可以有多个null
 * 3.HashMap中的key不能重复,value可以重复
 * 4.HashMap是线程不安全的
 * ------------------------------------------------------------------
 * HashMap的底层是：数组+链表+红黑树
 * 数组：初始化后,默认大小为16
 * 链表：新增的每一个元素都是一个HashMap$Node,它实现了Map$Entry
 *       每一个Node里面有属性next,指向下一个元素,从而形成链表
 * 红黑树：当一条链表上的元素>8,且table表的大小>=64,就会将该链表转为红黑树
 * ------------------------------------------------------------------
 * 扩容机制：同HashSet一样,因为HashSet的底层就是HashMap
 * 1.HashMap底层维护了Node类型的table数组,默认为null
 * 2.当创建对象时new HashMap(),loadfactor(加载因子)初始化为0.75
 * 3.当添加时put(k,v),首先通过key计算出hash值得到table表所在的索引位置。
 *   然后判断该索引处是否有元素,如果没有直接添加
 *   如果有元素,则判断该位置元素的key是否和准备加入元素的key相同,
 *   如果相同,则直接替换value,
 *   如果key不相同,则需判断当前所在位置是链表结构还是树结构,
 *   如果添加时容量不够,则扩容
 * 4.第一次添加时,table的默认大小为16,threshold(临界值)为16 * 0.75 = 12
 *   之后添加达到临界值,
 *   扩容table的大小为原来的2倍:16*2 = 32,
 *   临界值也为原来的2倍:32*0.75 = 12*2 = 24,依次类推
 */
public class Detail_HashMap {
    public static void main(String[] args) {
        //示例1,
        Map<String,String> map1 = new HashMap<>();
        map1.put("001","java");//OK
        map1.put("002","php");//OK
        map1.put("001","javaScript");//替换第一个元素

        //示例2,演示链表转成红黑树
        Map<A,String> map2 = new HashMap<>();
        for (int i = 1; i <= 12; i++) {
            /**
             * 因为重写了hashCode方法,执行put()时,元素会放在table表中的同一条链表上
             * 当i=9时,table表会扩容到：16*2 = 32,临界值为：32*0.75 = 24
             * 当i=10时,table表会扩容到：32*2 = 64,临界值为：64*0.75 = 48
             * 当i=11时,此时满足同一条链表上元素 > 8,且table表的大小 >= 64
             * 不在进行扩容,该链表就会变成红黑树,此时HashMap$Node -->HashMap$TreeNode
             */
            map2.put(new A(i),"哈哈哈");
        }
    }
}

//重写hashCode方法,执行put()时,元素会放在table表中的同一条链表上
//当该条链表的元素 > 8 且table表的大小 >= 64,该链表就会变成红黑树
class A{
    private Integer num;

    public Integer getNum() {
        return this.num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public A(Integer num) {
        this.num = num;
    }

    @Override
    public int hashCode() {
        return 100;
    }
}
